package it.megasoft78.turingmachine;

import it.megasoft78.turingmachine.TuringMachineEngine.TuringMachineAction;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.ToggleButton;

public class TuringMachineActivity extends Activity implements OnClickListener,
		OnItemClickListener {
	public static final String TAG = "TuringMachine";
	private final int NCELLS_TAPE = 100;
	private final char BLANK_CHAR = '#';
	private final int TimeStep = 100;

	private ToggleButton btnRunStop;
	private LinearLayout layTapeContainer;
	private HorizontalScrollView layTapeScroll;
	private TextView lblDescription;
	private EditText txtInput;
	private ListView lstTuples;
	private ArrayList<TupleEntity> tuples;
	private ArrayList<String> moveTypes;
	private final Handler handler = new Handler();
	private Runnable sendUpdatesToUI;
	private TuringMachineEngine _engine;
	public static final int REQUEST_CODE_ADD_EDIT_TUPLE = 1;
	public static final int REQUEST_CODE_OPEN_SAVE_FILE = 2;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

		lblDescription = (TextView) findViewById(R.id.lblDescription);

		btnRunStop = (ToggleButton) findViewById(R.id.btnRunStop);
		btnRunStop.setOnClickListener(this);
		btnRunStop.requestFocus();

		txtInput = (EditText) findViewById(R.id.txtInput);

		moveTypes = new ArrayList<String>(Arrays.asList(getResources()
				.getStringArray(R.array.arrMoveTypes)));

		tuples = new ArrayList<TupleEntity>();

		lstTuples = (ListView) findViewById(R.id.lstTuples);

		lstTuples.setOnItemClickListener(this);

		layTapeContainer = (LinearLayout) findViewById(R.id.layTapeContainer);
		layTapeScroll = (HorizontalScrollView) findViewById(R.id.layTapeScroll);

		for (int t = 0; t < NCELLS_TAPE; t++) {
			TextView lblCell = new TextView(this);
			lblCell.setGravity(Gravity.CENTER);
			lblCell.setText(String.valueOf(BLANK_CHAR));
			lblCell.setTextSize(25);
			lblCell.setBackgroundResource(R.drawable.cell_border);
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
					30, 30);

			if (t == 0)
				params.setMargins(5, 5, 0, 5);
			else if (t == NCELLS_TAPE - 1)
				params.setMargins(-1, 5, 5, 5);
			else
				params.setMargins(-1, 5, 0, 5);

			lblCell.setLayoutParams(params);
			layTapeContainer.addView(lblCell);
		}

		sendUpdatesToUI = new Runnable() {
			public void run() {
				boolean ok = true;
				TuringMachineAction action = _engine.nextStep();

				if (action != null) {
					TextView lblCell = (TextView) layTapeContainer
							.getChildAt(action.position);
					lblCell.setText(String.valueOf(action.printSymbol));
					lblCell.setBackgroundResource(R.drawable.cell_border);

					TextView lblCellNext = (TextView) layTapeContainer
							.getChildAt(action.nextPosition);
					lblCellNext
							.setBackgroundResource(R.drawable.cell_border_selected);

					int left = lblCellNext.getLeft();
					int width = lblCellNext.getWidth();

					if (left > layTapeScroll.getWidth())
						layTapeScroll.scrollTo(
								left + width - layTapeScroll.getWidth(), 0);

					lblCellNext.scrollTo(0, 0);

					if (action.end)
						ok = false;

				} else {
					ok = false;
				}

				if (ok) {
					handler.postDelayed(this, TimeStep);
				} else {
					TuringMachineActivity.this.btnRunStop.setChecked(false);
					TuringMachineActivity.this.onClick(btnRunStop);
				}
			}
		};

		this.openTuringFile("/sdcard/Turing/copia.tur");
	}

	private void hideVirtualKeyboard(EditText edit) {
		InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
		imm.hideSoftInputFromWindow(edit.getWindowToken(), 0);
	}

	@Override
	protected void onResume() {
		super.onResume();

		LinearLayout layInputContainer = (LinearLayout) findViewById(R.id.layInputContainer);
		layInputContainer.requestFocus();
	}

	private void fillTuples(ArrayList<TupleEntity> tuples) {
		ArrayList<HashMap<String, Object>> data = new ArrayList<HashMap<String, Object>>();

		for (int t = 0; t < tuples.size(); t++) {
			TupleEntity tuple = tuples.get(t);

			HashMap<String, Object> item = new HashMap<String, Object>();
			item.put("CurrentState", tuple.getCurrentState());
			item.put("ScannedSymbol", tuple.getScannedSymbol());
			item.put("PrintSymbol", tuple.getPrintSymbol());
			item.put("MoveTape", moveTypes.get(tuple.getMoveTape()));
			item.put("FinalState", tuple.getFinalState());

			data.add(item);
		}

		String[] from = { "CurrentState", "ScannedSymbol", "PrintSymbol",
				"MoveTape", "FinalState" };
		int[] to = { R.id.lblCurrentState, R.id.lblScannedSymbol,
				R.id.lblPrintSymbol, R.id.lblMoveTape, R.id.lblFinalState };

		SimpleAdapter adapter = new SimpleAdapter(getApplicationContext(),
				data, R.layout.tuple_item, from, to);
		lstTuples.setAdapter(adapter);
	}

	@Override
	public void onClick(View view) {
		switch (view.getId()) {
		case R.id.btnRunStop:

			if (btnRunStop.isChecked()) {
				this.setTextOnTape(txtInput.getText().toString());
				txtInput.setEnabled(false);
				this.hideVirtualKeyboard(txtInput);

				_engine = new TuringMachineEngine(tuples, this.getTapeCells());
				TextView lblCell = (TextView) layTapeContainer.getChildAt(0);
				lblCell.setBackgroundResource(R.drawable.cell_border_selected);

				handler.removeCallbacks(sendUpdatesToUI);
				handler.postDelayed(sendUpdatesToUI, 0);
			} else {
				handler.removeCallbacks(sendUpdatesToUI);

				txtInput.setEnabled(true);
			}
			break;

		default:
			break;
		}
	}

	private char[] getTapeCells() {
		char[] retVal = new char[layTapeContainer.getChildCount()];

		for (int t = 0; t < layTapeContainer.getChildCount(); t++) {
			TextView lblCell = (TextView) layTapeContainer.getChildAt(t);

			retVal[t] = lblCell.getText().charAt(0);
		}

		return retVal;
	}

	private void setTextOnTape(String input) {
		char[] chrInput = input.toCharArray();

		for (int t = 0; t < layTapeContainer.getChildCount(); t++) {
			TextView lblCell = (TextView) layTapeContainer.getChildAt(t);

			if (t < chrInput.length)
				if (chrInput[t] == ' ')
					lblCell.setText(String.valueOf(BLANK_CHAR));
				else
					lblCell.setText(String.valueOf(chrInput[t]));
			else
				lblCell.setText(String.valueOf(BLANK_CHAR));
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.main_menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		Intent intent;

		switch (item.getItemId()) {

		case R.id.mnuNew:
			break;

		case R.id.mnuOpen:
			intent = new Intent(TuringMachineActivity.this,
					FileDialogActivity.class);
			intent.putExtra(FileDialogActivity.START_PATH, "/sdcard");
			intent.putExtra(FileDialogActivity.MODE,
					FileDialogActivity.MODE_OPEN_FILE);
			TuringMachineActivity.this.startActivityForResult(intent,
					TuringMachineActivity.REQUEST_CODE_OPEN_SAVE_FILE);
			break;

		case R.id.mnuSave:
			break;

		case R.id.mnuDelete:
			break;

		case R.id.mnuHelp:
			intent = new Intent(TuringMachineActivity.this, HelpActivity.class);
			TuringMachineActivity.this.startActivity(intent);
			break;

		case R.id.mnuAbout:
			intent = new Intent(TuringMachineActivity.this, AboutActivity.class);
			TuringMachineActivity.this.startActivity(intent);
			break;

		default:
			return super.onOptionsItemSelected(item);
		}

		return true;
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position,
			long id) {
		Intent intent = new Intent(TuringMachineActivity.this,
				EditTupleActivity.class);
		intent.putExtra("Tuples", tuples);
		intent.putExtra("SelectedIndex", position);
		intent.putExtra("Mode", EditTupleActivity.EDIT_TUPLE);
		TuringMachineActivity.this.startActivityForResult(intent,
				TuringMachineActivity.REQUEST_CODE_ADD_EDIT_TUPLE);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);

		switch (requestCode) {
		case TuringMachineActivity.REQUEST_CODE_ADD_EDIT_TUPLE:
			if (resultCode == Activity.RESULT_OK) {
				int mode = data.getExtras().getInt("Mode");

				if (mode == EditTupleActivity.ADD_TUPLE) {

				} else if (mode == EditTupleActivity.EDIT_TUPLE) {
					int selectedIndex = data.getExtras()
							.getInt("SelectedIndex");
					TupleEntity changedTuple = (TupleEntity) data.getExtras()
							.getSerializable("ChangedTuple");

					tuples.set(selectedIndex, changedTuple);

					this.fillTuples(tuples);
				}
			}
			break;

		case TuringMachineActivity.REQUEST_CODE_OPEN_SAVE_FILE:
			if (resultCode == Activity.RESULT_OK) {
				String fileName = data.getExtras().getString("FileName");
				this.openTuringFile(fileName);
			}
			break;
		}
	}

	private void openTuringFile(String fileName) {
		try {
			BufferedReader br = new BufferedReader(new FileReader(fileName));

			String text;

			int index = 0;

			while ((text = br.readLine()) != null) {
				if (index == 0) {
					lblDescription.setText(text);
				} else if (index == 1) {
					txtInput.setText(text);
				} else {
					String[] values = text.split("\\|");

					TupleEntity tuple = new TupleEntity();
					tuple.setCurrentState(values[0]);
					tuple.setScannedSymbol(values[1]);
					tuple.setFinalState(values[2]);
					tuple.setPrintSymbol(values[3]);
					tuple.setMoveTape(this.getMoveTape(values[4]));

					tuples.add(tuple);
				}

				index++;
			}

			this.fillTuples(tuples);

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private int getMoveTape(String value) {

		if (value.compareTo("<") == 0)
			return TupleEntity.LEFT;
		else if (value.compareTo(">") == 0)
			return TupleEntity.RIGHT;
		else
			return TupleEntity.NONE;
	}
}